
// SmpTst.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include "windows.h"

#define DEF_SMPIO_CONSOLE		// use debugout as the default io
#define KEN_LIB_IMP
#define MS_VC_CMPILE
#include "SmpIO.h"
#include "SmpTst.h"
#include "CmdLnPars.h"

#include <iostream>

typedef BOOL (*pfnEachTstThrdCntx)(DWORD id, HANDLE h);
class CTstThrdCntxS
{
public:
	CTstThrdCntxS()
		:m_dwMaxId(0)
		,m_hEmptyEvnt(NULL)
	{
		InitializeCriticalSection(&m_cs);
	};

	~CTstThrdCntxS();

public:
	DWORD NewThrdCntx();
	BOOL DeleteThrdCntx(DWORD id);

	HANDLE GetThrdCntxEvnt(DWORD id);

public:
	BOOL ForEachCntx(pfnEachTstThrdCntx pfn);
	VOID WaitTillEmpty();
//	BOOL WaitEnd(DWORD id);

private:
	CRITICAL_SECTION m_cs;
	DWORD m_dwMaxId;

	map<DWORD, HANDLE> m_mapCntx;

	HANDLE m_hEmptyEvnt;
};

CTstThrdCntxS::~CTstThrdCntxS()
{
	for(map<DWORD, HANDLE>::iterator it=m_mapCntx.begin(); it!=m_mapCntx.end(); it++)
	{
		CloseHandle(it->second);
	}

	DeleteCriticalSection(&m_cs);
}

DWORD CTstThrdCntxS::NewThrdCntx()
{
	EnterCriticalSection(&m_cs);
	HANDLE h=CreateEvent(NULL, FALSE, FALSE, NULL);
	m_mapCntx.insert(pair<DWORD, HANDLE>(m_dwMaxId, h));
	m_dwMaxId++;
	LeaveCriticalSection(&m_cs);
	return m_dwMaxId-1;
}

BOOL CTstThrdCntxS::DeleteThrdCntx(DWORD id)
{
	EnterCriticalSection(&m_cs);
	map<DWORD, HANDLE>::iterator it=m_mapCntx.find(id);
	if(it==m_mapCntx.end())
	{
		LeaveCriticalSection(&m_cs);
		return FALSE;
	}

	CloseHandle(it->second);
	m_mapCntx.erase(it);
	LeaveCriticalSection(&m_cs);

	if(m_mapCntx.empty() && m_hEmptyEvnt )
	{
		SetEvent(m_hEmptyEvnt);
	}

	return TRUE;
}

HANDLE CTstThrdCntxS::GetThrdCntxEvnt(DWORD id)
{
	EnterCriticalSection(&m_cs);
	map<DWORD, HANDLE>::iterator it=m_mapCntx.find(id);
	if(it==m_mapCntx.end())
	{
		LeaveCriticalSection(&m_cs);
		return NULL;
	}

	LeaveCriticalSection(&m_cs);
	return it->second;
}

BOOL CTstThrdCntxS::ForEachCntx(pfnEachTstThrdCntx pfn)
{
	BOOL fRet=TRUE;

	EnterCriticalSection(&m_cs);
	for(map<DWORD, HANDLE>::iterator it=m_mapCntx.begin(); it!=m_mapCntx.end(); it++)
	{
		if(!(*pfn)(it->first, it->second))
		{
			fRet=FALSE;
			break;
		}
	}
	LeaveCriticalSection(&m_cs);

	return fRet;
}

VOID CTstThrdCntxS::WaitTillEmpty()
{
	if(m_mapCntx.empty())
	{
		return;
	}

	m_hEmptyEvnt=CreateEvent(NULL, FALSE, FALSE, NULL);
	WaitForSingleObject(m_hEmptyEvnt, INFINITE);
}

typedef struct TstThreadPara
{
	CSmpIO* pSmpIO;
	CSmpTester* pTster;
	CTstThrdCntxS* pThrdCntxs;
}TstThreadPara;


BOOL CancleEachTst(DWORD id, HANDLE h)
{
	SetEvent(h);
	return TRUE;
}

BOOL ListTesting(DWORD id, HANDLE h)
{
	g_SmpIO.Printf(_T("testing id:%d\r\n"), id);
	return TRUE;
}


//
// io options
//
bool gIO_fSetOutput;
bool gIO_fOConsole;
bool gIO_fODbugOut;
bool gIO_fOATL;
_tstring gIO_strOSocket;
DWORD gIO_dwOSocketPort;
bool gIO_fOWindow;
bool gIO_fOCelog;
DWORD gIO_dwOSerial;
_tstring gIO_strOFile;

bool gIO_fSetInput;
bool gIO_fIConsole;
_tstring gIO_strISocket;
DWORD gIO_dwISocketPort;
bool gIO_fIWindow;
DWORD gIO_dwISerial;
_tstring gIO_strIFile;

CMDLINE_INIT_NAME_VAL(_T("_output"), _tstring, gIO_strOutputOptions, _T(""), TEXT("USE \"/_var:output_\" TO LIST OUTPUT OPTIONS\r\n"), FALSE);
CMDLINE_INIT_EXT_NAME_VAL(_T("output_console"), bool, gIO_fOConsole, TEXT("console as output\r\n"), FALSE);
CMDLINE_INIT_EXT_NAME_VAL(_T("output_debugout"), bool, gIO_fODbugOut, TEXT("OutputDebugString as output\r\n"), FALSE);
CMDLINE_INIT_EXT_NAME_VAL(_T("output_file"), _tstring, gIO_strOFile, TEXT("file:FILE_NAME as output\r\n"), FALSE);
CMDLINE_INIT_EXT_NAME_VAL(_T("output_atl"), bool, gIO_fOATL, TEXT("ATL as output\r\n"), FALSE);
CMDLINE_INIT_EXT_NAME_VAL(_T("output_socket"), _tstring, gIO_strOSocket, TEXT("socket:xxx.xxx.xxx.xxx as output\r\n"), FALSE);
CMDLINE_INIT_EXT_NAME_VAL(_T("output_socket_port"), DWORD, gIO_dwOSocketPort, TEXT("socket port of output\r\n"), FALSE);
CMDLINE_INIT_EXT_NAME_VAL(_T("output_window"), bool, gIO_fOWindow, TEXT("window as output\r\n"), FALSE);
CMDLINE_INIT_EXT_NAME_VAL(_T("output_celog"), bool, gIO_fOCelog, TEXT("output_celog as output\r\n"), FALSE);
CMDLINE_INIT_EXT_NAME_VAL(_T("output_serial"), DWORD, gIO_dwOSerial, TEXT("serial:COM_NUMB as output\r\n"), FALSE);
CMDLINE_INIT_EXT_NAME_VAL(_T("output_set"), bool, gIO_fSetOutput, TEXT("set output\r\n"), TRUE);

CMDLINE_INIT_NAME_VAL(_T("_input"), _tstring, gIO_strInputOptions, _T(""), TEXT("USE \"/_var:input_\" TO LIST INPUT OPTIONS\r\n"), FALSE);
CMDLINE_INIT_EXT_NAME_VAL(_T("input_console"), bool, gIO_fIConsole, TEXT("console as input\r\n"), FALSE);
CMDLINE_INIT_EXT_NAME_VAL(_T("input_file"), _tstring, gIO_strIFile, TEXT("file:FILE_NAME as input\r\n"), FALSE);
CMDLINE_INIT_EXT_NAME_VAL(_T("input_socket"), _tstring, gIO_strISocket, TEXT("socket:xxx.xxx.xxx.xxx as input\r\n"), FALSE);
CMDLINE_INIT_EXT_NAME_VAL(_T("input_socket_port"), DWORD, gIO_dwISocketPort, TEXT("socket port of input\r\n"), FALSE);
CMDLINE_INIT_EXT_NAME_VAL(_T("input_window"), bool, gIO_fIWindow, TEXT("window as input\r\n"), FALSE);
CMDLINE_INIT_EXT_NAME_VAL(_T("input_serial"), DWORD, gIO_dwISerial, TEXT("serial:COM_NUMB as input\r\n"), FALSE);
CMDLINE_INIT_EXT_NAME_VAL(_T("input_set"), bool, gIO_fSetInput, TEXT("set input\r\n"), TRUE);

VOID CheckSmpIOVar(CCmdLineParser* pCmdLn, CSmpIO* pSmpIO)
{
}
void DoSmpIO(CSmpTester* pTster, CCmdLineParser* pCmdLn, CSmpIO* pSmpIO, CTstThrdCntxS* pThrdCntxs)
{
	// build io
	if(gIO_fSetOutput)
	{
		if(gIO_fOConsole)
		{
			pSmpIO->EnBuildInIO(FALSE, SMPIO_CONSOLE, SMIO_TYPE_OUT);
			pSmpIO->EnBuildInIO(TRUE, SMPIO_CONSOLE, SMIO_TYPE_OUT);
		}
		if(gIO_fODbugOut)
		{
			pSmpIO->EnBuildInIO(FALSE, SMPIO_DBGOUT, SMIO_TYPE_OUT);
			pSmpIO->EnBuildInIO(TRUE, SMPIO_DBGOUT, SMIO_TYPE_OUT);
		}
		if(gIO_fOATL)
		{
			pSmpIO->EnBuildInIO(FALSE, SMPIO_ATL, SMIO_TYPE_OUT);
			pSmpIO->EnBuildInIO(TRUE, SMPIO_ATL, SMIO_TYPE_OUT);
		}
		if(!gIO_strOSocket.empty())
		{
			pSmpIO->EnBuildInIO(FALSE, SMPIO_SOCKET, SMIO_TYPE_OUT);
			pSmpIO->EnBuildInIO(TRUE, SMPIO_SOCKET, SMIO_TYPE_OUT);
		}
		if(gIO_fOWindow)
		{
			pSmpIO->EnBuildInIO(FALSE, SMPIO_WINDOWS, SMIO_TYPE_OUT);
			pSmpIO->EnBuildInIO(TRUE, SMPIO_WINDOWS, SMIO_TYPE_OUT);
		}
		if(gIO_fOCelog)
		{
			pSmpIO->EnBuildInIO(FALSE, SMPIO_CELOG, SMIO_TYPE_OUT);
		}
		if(gIO_dwOSerial)
		{
			pSmpIO->EnBuildInIO(FALSE, SMPIO_SERIAL, SMIO_TYPE_OUT);
			pSmpIO->EnBuildInIO(TRUE, SMPIO_SERIAL, SMIO_TYPE_OUT);
		}
		if(!gIO_strOFile.empty())
		{
			pSmpIO->EnBuildInIO(FALSE, SMPIO_FILE, SMIO_TYPE_OUT);
			pSmpIO->EnBuildInIO(TRUE, SMPIO_FILE, SMIO_TYPE_OUT);
		}
	}
	if(gIO_fSetInput)
	{
		if(gIO_fIConsole)
		{
			pSmpIO->EnBuildInIO(FALSE, SMPIO_CONSOLE, SMIO_TYPE_IN);
			pSmpIO->EnBuildInIO(TRUE, SMPIO_CONSOLE, SMIO_TYPE_IN);
		}
		else if(!gIO_strIFile.empty())
		{
			pSmpIO->EnBuildInIO(FALSE, SMPIO_FILE, SMIO_TYPE_IN);
			pSmpIO->EnBuildInIO(TRUE, SMPIO_FILE, SMIO_TYPE_IN);
		}
		else if(!gIO_strOSocket.empty())
		{
			pSmpIO->EnBuildInIO(FALSE, SMPIO_SOCKET, SMIO_TYPE_IN);
			pSmpIO->EnBuildInIO(TRUE, SMPIO_SOCKET, SMIO_TYPE_IN);
		}
		else if(gIO_fIWindow)
		{
			pSmpIO->EnBuildInIO(FALSE, SMPIO_WINDOWS, SMIO_TYPE_IN);
			pSmpIO->EnBuildInIO(TRUE, SMPIO_WINDOWS, SMIO_TYPE_IN);
		}
		else if(gIO_dwISerial)
		{
			pSmpIO->EnBuildInIO(FALSE, SMPIO_SERIAL, SMIO_TYPE_IN);
			pSmpIO->EnBuildInIO(TRUE, SMPIO_SERIAL, SMIO_TYPE_IN);
		}
	}
}

//
// test options
//

bool gTST_fListCase;
_tstring gTST_strListVar;
_tstring gTST_strDumpVar;
bool gTST_fListResult;
bool gTST_fStartCases;
bool gTST_fStartCasesAsync;
bool gTST_fAsync;
DWORD gTST_dwStopCase;
_tstring gTST_strCases;
DWORD gTST_dwTO;
DWORD gTST_dwTimes;
DWORD gTST_dwPriority;
int gTST_iType;
bool gTST_fList;

// test fram vars
CMDLINE_INIT_NAME_VAL(_T("_tst"), _tstring, gTST_strTstOptions, _T(""), TEXT("USE \"/_var:_\" TO LIST TEST OPTIONS\r\n"), FALSE);
CMDLINE_INIT_EXT_NAME_VAL(_T("_list"), bool, gTST_fListCase, TEXT("list test case\r\n"), TRUE);
CMDLINE_INIT_EXT_NAME_VAL(_T("_var"), _tstring, gTST_strListVar, TEXT("list test vars[:VAR_PREFIX]\r\n"), TRUE);
CMDLINE_INIT_EXT_NAME_VAL(_T("_dmp_var"), _tstring, gTST_strDumpVar, TEXT("dump vars:VAR_NAME\r\n"), TRUE);
CMDLINE_INIT_EXT_NAME_VAL(_T("_rslt"), bool, gTST_fListResult, TEXT("list test result\r\n"), TRUE);
CMDLINE_INIT_EXT_NAME_VAL(_T("_start"), bool, gTST_fStartCases, TEXT("start test cases\r\n"), TRUE);
CMDLINE_INIT_EXT_NAME_VAL(_T("_start_async"), bool, gTST_fStartCasesAsync, TEXT("start test cases without wait end\r\n"), TRUE);
CMDLINE_INIT_EXT_NAME_VAL(_T("_async"), bool, gTST_fAsync, TEXT("do test cases async\r\n"), FALSE);
CMDLINE_INIT_EXT_NAME_VAL(_T("_stop"), DWORD, gTST_dwStopCase, TEXT("stop case :CASE_NUMB\r\n"), TRUE);
CMDLINE_INIT_EXT_NAME_VAL(_T("_case"), _tstring, gTST_strCases, TEXT("add test :CASE_NAME to test\r\n"), FALSE);
CMDLINE_INIT_EXT_NAME_VAL(_T("_timeout"), DWORD, gTST_dwTO, TEXT("test time out:TIME_OUT\r\n"), FALSE);
CMDLINE_INIT_EXT_NAME_VAL(_T("_times"), DWORD, gTST_dwTimes, TEXT("do test case :TST_TIMES times\r\n"), FALSE);
CMDLINE_INIT_EXT_NAME_VAL(_T("_priority"), DWORD, gTST_dwPriority, TEXT("do test case with priority :PRIORITY\r\n"), FALSE);
CMDLINE_INIT_EXT_NAME_VAL(_T("_type"), int, gTST_iType, TEXT("SMPTST_CASE_NODE:0 SMPTST_CASE_TREE:1 SMPTST_CASE_DEEP_TREE:2\r\n"), FALSE);
CMDLINE_INIT_EXT_NAME_VAL(_T("_tsting"), bool, gTST_fList, TEXT("list testing case\r\n"), TRUE);


VOID CheckTstVar(CCmdLineParser* pCmdLn, CSmpIO* pSmpIO)
{
	gTST_dwStopCase=INVALID_DWORD;
	pCmdLn->GetOption(_T("tst_stop"), &gTST_dwStopCase);

	if( (gTST_iType>SMPTST_CASE_DEEP_TREE) || (gTST_iType<SMPTST_CASE_NODE) )
	{
		pSmpIO->Printf(TEXT("invalide type option:%d\r\n"), gTST_iType);
		gTST_iType=SMPTST_CASE_NODE;
	}


	gTST_dwPriority=INVALID_DWORD;
	pCmdLn->GetOption(_T("priority"), &gTST_dwPriority);
	if( (INVALID_DWORD!=gTST_dwPriority)
		&& (gTST_dwPriority>255) )
	{
		pSmpIO->Printf(TEXT("invalide priority:%d\r\n"), gTST_dwPriority);
		gTST_dwPriority=INVALID_DWORD;
	}

	if(0==gTST_dwTimes)
	{
		gTST_dwTimes=1;
	}
}


DWORD WINAPI TstThread(LPVOID lpParameter)
{
	TstThreadPara* pTstThreadPara=(TstThreadPara*)lpParameter;
	CSmpIO* pSmpIO=pTstThreadPara->pSmpIO;
	CSmpTester* pTster=pTstThreadPara->pTster;
	CTstThrdCntxS* pThrdCntxs=pTstThreadPara->pThrdCntxs;
	delete pTstThreadPara;	//! if there is some good methed to do this?

	DWORD dwCntxId=pThrdCntxs->NewThrdCntx();
	HANDLE hEvent=pThrdCntxs->GetThrdCntxEvnt(dwCntxId);
	_tstring strCases(gTST_strCases);
	BOOL fCanceld=FALSE;

	pSmpIO->Printf(_T("<<== do test(async) id:%d cases:%s\r\n"), dwCntxId, gTST_strCases);
	// do test case
	for(DWORD i=0; i<gTST_dwTimes; i++)
	{
		if(!pTster->DoTest(gTST_strCases.c_str(), (SMPTST_CASE)gTST_iType, !gTST_fAsync, gTST_dwTO, gTST_dwPriority, pSmpIO, hEvent))
		{
			break;
		}
	}
	pSmpIO->Printf(_T("==>> test(async) id:%d cases:%s end!\r\n"), dwCntxId, strCases.c_str());

	pThrdCntxs->DeleteThrdCntx(dwCntxId);

	return 0;
}

void DoTst(CSmpTester* pTster, CCmdLineParser* pCmdLn, CSmpIO* pSmpIO, CTstThrdCntxS* pThrdCntxs)
{
	BOOL fFirstTest=TRUE;

	//
	// list command
	if(gTST_fListCase)
	{
		pSmpIO->Printf(_T("<<== list test cases:%s\r\n"), gTST_strCases);
		pTster->Print(gTST_strCases.c_str(), FALSE, pSmpIO);
		pSmpIO->Puts(_T("==>>\r\n"));
	}
	else if(pCmdLn->GetOption(_T("_var"), &gTST_strListVar))
//	else if(!gTST_strListVar.empty())
	{
		pSmpIO->Puts(_T("Test case options:\r\n"));
		PrintCmdLnInitVarsInfo(pSmpIO, gTST_strListVar.c_str());
		pSmpIO->Puts(_T("==>>\r\n"));
	}
	else if(!gTST_strDumpVar.empty())
	{
		pSmpIO->Printf(_T("<<== dump var:%s\r\n"), gTST_strDumpVar.c_str());
		DumpCmdLnInitVar(pSmpIO, gTST_strDumpVar.c_str());
		pSmpIO->Puts(_T("==>>\r\n"));
	}	
	//test command sync
	else if(gTST_fStartCases)
	{
		if(gTST_strCases.empty())
		{
			pSmpIO->Printf(_T("Please specify test cases!\r\n"));
		}
		else
		{
			// do global init
			if(fFirstTest)
			{
				fFirstTest=false;
				pTster->DoInitCleanFunc(TRUE, 0, &g_SmpIO);
			}

			pSmpIO->Printf(_T("<<== do test(sync) cases:%s\r\n"), gTST_strCases.c_str());
			// do test case
			for(DWORD i=0; i<gTST_dwTimes; i++)
			{
				if(!pTster->DoTest(gTST_strCases.c_str(), (SMPTST_CASE)gTST_iType, !gTST_fAsync, gTST_dwTO, gTST_dwPriority, pSmpIO, NULL))
				{
					break;
				}
			}
			pSmpIO->Printf(_T("==>> test(sync) cases:%s end!\r\n"), gTST_strCases);
		}
	}
	//test command async
	else if(gTST_fStartCasesAsync)
	{
		if(gTST_strCases.empty())
		{
			pSmpIO->Printf(_T("Please specify test cases!\r\n"));
		}
		else
		{
			// do global init
			if(fFirstTest)
			{
				fFirstTest=false;
				pTster->DoInitCleanFunc(TRUE, 0, &g_SmpIO);
			}

			TstThreadPara* para=new (TstThreadPara);	// note to delete in thread
			para->pSmpIO=pSmpIO;
			para->pTster=pTster;
			para->pThrdCntxs=pThrdCntxs;
			CreateThread(NULL, 0, TstThread, para, 0, NULL);
		}
	}
	else if(gTST_dwStopCase!=INVALID_DWORD)
	{
		HANDLE hEvent=pThrdCntxs->GetThrdCntxEvnt(gTST_dwStopCase);
		if(NULL==hEvent)
		{
			pSmpIO->Printf(_T("not find test cases id:%d\r\n"), gTST_dwStopCase);
		}
		else
		{
			pSmpIO->Printf(_T("Try to cancle test cases id:%d\r\n"), gTST_dwStopCase);
			SetEvent(hEvent);
		}
	}
	else if(gTST_fListResult)
	{
		pSmpIO->Puts(_T("<<== print test results\r\n"));
		pSmpIO->Puts(_T("all result of test:\r\n"));
		pSmpIO->IncDeep();
		pTster->Print(NULL, TRUE, pSmpIO);
		pSmpIO->DecDeep();
		pSmpIO->Puts(_T("==>>\r\n"));
	}
	else if(gTST_fList)
	{
		pSmpIO->Puts(_T("<<== list all testing case:\r\n"));
		pSmpIO->IncDeep();
		pThrdCntxs->ForEachCntx(ListTesting);
		pSmpIO->DecDeep();
		pSmpIO->Puts(_T("==>>\r\n"));
	}
}

const static TCHAR* s_pszUsage=
_T("test tools base commands:\r\n")\
_T("\tinteract\r\n")\
_T("\thelp\r\n")\
_T("\texit\r\n");

CSmpTester g_Tster;
CCmdLineParser g_CmdLnParser;
CTstThrdCntxS g_TstThrdCntxS;

VOID Usage(CSmpIO* pSmpIO, CSmpTester* pTster)
{
	pSmpIO->Puts(s_pszUsage);
//	pSmpIO->IncDeep();
	pSmpIO->Printf(_T("<<== list test cases:%s\r\n"), gTST_strCases);
	pTster->Print(NULL, FALSE, pSmpIO);
	pSmpIO->Puts(_T("==>>\r\n"));
	pSmpIO->Puts(_T("Test case options:\r\n"));
	PrintCmdLnInitVarsInfo(pSmpIO, TEXT("_"));
	pSmpIO->Puts(_T("==>>\r\n"));
//	pSmpIO->DecDeep();
}


#if (defined(WIN32) && defined(_CONSOLE))
int _tmain(int argc, const _TCHAR* argv[])
{
	// parse command options
	g_CmdLnParser.ParseArg(argc, argv);
#else
int APIENTRY _tWinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPTSTR    lpCmdLine,
                     int       nCmdShow)
{
//	AllocConsole();

	// parse command options
	g_CmdLnParser.ParseCmdLine(lpCmdLine);
#endif

	// build up test tree
	BuildUpDefTester(&g_Tster);

	CCmdLineParser* pCmdLineParser=&g_CmdLnParser;
	BOOL fFirstTest=TRUE;
	TCHAR szCmdLnBuf[512];
	bool fInteract=false;
	CCmdLineParser cInputParser;
	do
	{
		// init globale var through command line
		BuildUpCmdLnInitVal(pCmdLineParser);

		if( (0==g_CmdLnParser.GetOptionCnt())
			|| pCmdLineParser->GetOption(_T("h"))
			|| pCmdLineParser->GetOption(_T("help"))
			|| pCmdLineParser->GetOption(_T("usage")) )
		{
			Usage(&g_SmpIO, &g_Tster);
			if(0==g_CmdLnParser.GetOptionCnt())
			{
				return 0;
			}
		}
		
		if(pCmdLineParser->GetOption(_T("exit")) )
		{
			g_TstThrdCntxS.ForEachCntx(CancleEachTst);
			break;
		}

		pCmdLineParser->GetOption(_T("interact"), &fInteract);

		// smpio related
		CheckSmpIOVar(pCmdLineParser, &g_SmpIO);
		DoSmpIO(&g_Tster, pCmdLineParser, &g_SmpIO, &g_TstThrdCntxS);

		// test related
		CheckTstVar(pCmdLineParser, &g_SmpIO);
		DoTst(&g_Tster, pCmdLineParser, &g_SmpIO, &g_TstThrdCntxS);

		if(fInteract)
		{
			cInputParser.Clear();
			g_SmpIO.Puts(TEXT("\r\nPlease input options:"));
			if(0==g_SmpIO.Gets(szCmdLnBuf, ARRAYOF(szCmdLnBuf)))
			{
				break;
			}
			g_SmpIO.Puts(TEXT(""));
			cInputParser.ParseCmdLine(szCmdLnBuf);
			pCmdLineParser=&cInputParser;
		}

	}while(fInteract);

	g_TstThrdCntxS.WaitTillEmpty();

	// do global init
	if(!fFirstTest)
	{
		// do global cleanup
		g_Tster.DoInitCleanFunc(FALSE, 0, &g_SmpIO);
		
		// print all result
		g_SmpIO.Puts(TEXT("all result of test:\r\n"));
		g_SmpIO.IncDeep();
		g_Tster.Print(NULL, TRUE, &g_SmpIO);
		g_SmpIO.DecDeep();
	}
	
	return 0;
}

